<template>
  <div id="global-uploader">
    <uploader
      ref="uploader"
      :options="options"
      :autoStart="false"
      :fileStatusText="fileStatusText"
      @file-added="onFileAdded"
      @file-success="onFileSuccess"
      @file-progress="onFileProgress"
      @file-error="onFileError"
      class="uploader-app"
    >
      <uploader-unsupport></uploader-unsupport>
      <!--上传按钮（隐藏）-->
      <uploader-btn id="global-uploader-btn" :attrs="attrs" ref="uploadBtn"></uploader-btn>
      <div v-show="panelShow">
        <!--上传面板-->
        <uploader-list v-show="!collapse">
          <div class="file-panel" slot-scope="props">
            <div class="file-title">
              <!-- 文件列表 -->
              <h2>{{$t('systerms.recovery.recovery_fileList')}}</h2>
              <div class="operate">
                <!-- recovery_fold -->
                <el-button
                  :title="$t('systerms.recovery.recovery_fold')"
                  @click="fileListShow"
                  type="text"
                  size="small"
                >{{$t('systerms.recovery.recovery_fold')}}</el-button>
              </div>
            </div>

            <ul class="file-list">
              <li v-for="file in props.fileList" :key="file.id">
                <uploader-file :class="'file_' + file.id" ref="files" :file="file" :list="true"></uploader-file>
              </li>
              <!-- 暂无待上传文件 -->
              <div class="no-file" v-if="!props.fileList.length">
                <i class="iconfont icon-empty-file"></i>
                {{$t('systerms.recovery.recovery_noUpfile')}}
              </div>
            </ul>
          </div>
        </uploader-list>
        <!--上传导航iocn-->
        <div class="icon-panel" v-show="collapse" @click="fileListShow">
          <i class="el-icon-upload" style="font-size: 20px;color: white;"></i>
        </div>
      </div>
    </uploader>
  </div>
</template>

<script>
/**
 *   全局上传插件
 *   调用方法：Bus.$emit('openUploader', {}) 打开文件选择框，参数为需要传递的额外参数
 *   监听函数：Bus.$on('fileAdded', fn); 文件选择后的回调
 *            Bus.$on('fileSuccess', fn); 文件上传成功的回调
 *           Bus.$on('open', fn); 打开上传面板的回调
 *           Bus.$on('close', fn); 关闭上传面板的回调
*/
import { ACCEPT_CONFIG } from './js/config';
import Bus from './js/bus';
import SparkMD5 from 'spark-md5';
import crypto from '@/utils/crypto';
import $ from 'jquery';
export default {
  components: {},
  data () {
    return {
      options: {
        // target: '/uapApi' + api,
        target: '/api/resource/fileUpload/all',
        singleFile: true, // 单文件上传
        chunkSize: '2097152', // 2M
        fileParameterName: 'upfile', // 上传文件时文件的参数名
        maxChunkRetries: 3, // 最大自动失败重试上传次数
        testChunks: true,   // 是否开启服务器分片校验
        // 服务器分片校验函数，秒传及断点续传基础
        checkChunkUploadedByResponse: function(chunk, message) {
          let objMessage = JSON.parse(message);
          // 是否是秒传
          if (objMessage.skipUpload) {
            return true;
          }
          return (objMessage.blocksNum || []).indexOf(chunk.offset + 1) >= 0;
        },
        headers: {
          'X-XSRF-TOKEN': crypto.getCsrf()
        }
      },
      attrs: {
        accept: ACCEPT_CONFIG.getAll()
      },
      panelShow: false,   // 选择文件后，展示上传panel
      collapse: false,
      chunkMD5List: [],
      // fileStatusText: {
      //   // 上传成功 上传出错 上传中... 暂停 等待中...
      //   success: this.$t('systerms.recovery.recovery_upFileSuccess'),
      //   error: this.$t('systerms.recovery.recovery_upFileSuccessError'),
      //   uploading: this.$t('systerms.recovery.recovery_upFileSuccessUploading'),
      //   paused: this.$t('systerms.recovery.recovery_upFileSuccessPaused'),
      //   waiting: this.$t('systerms.recovery.recovery_upFileWaiting'),
      // }
    };
  },
  mounted () {
    Bus.$on('openUploader', query => {
      this.params = query || {};
      if (this.$refs.uploadBtn) {
        $('#global-uploader-btn').click();
      }
    });
    // 监听函数
    Bus.$on('open', () => {
      // this.uploader.cancel();
      this.panelShow = true;
      this.collapse = true;
    });
    Bus.$on('close', () => {
      this.panelShow = false;
      this.collapse = false;
    });
  },
  computed: {
    // Uploader实例
    uploader () {
      return this.$refs.uploader.uploader;
    },
    fileStatusText () {
      return {
        // 上传成功 上传出错 上传中... 暂停 等待中...
        success: this.$t('systerms.recovery.recovery_upFileSuccess'),
        error: this.$t('systerms.recovery.recovery_upFileSuccessError'),
        uploading: this.$t('systerms.recovery.recovery_upFileSuccessUploading'),
        paused: this.$t('systerms.recovery.recovery_upFileSuccessPaused'),
        waiting: this.$t('systerms.recovery.recovery_upFileWaiting'),
      };
    }
  },
  destroyed () {
    Bus.$off('openUploader');
  },
  methods: {
    onFileAdded (file) {
      this.collapse = false;
      this.computeMD5(file);
      Bus.$emit('fileAdded');
    },
    onFileProgress (rootFile, file, chunk) {
    },
    onFileSuccess (rootFile, file, response, chunk) {
      let res = JSON.parse(response);
      // 服务器自定义的错误（即虽返回200，但是是错误的情况），这种错误是Uploader无法拦截的
      if (!res.result) {
        this.$message.error(res.message);
        // 文件状态设为“失败”
        this.statusSet(file.id, 'failed');
        return;
      }
      // 如果服务端返回需要合并
      if (res.needMerge) {
        // 文件状态设为“合并中”
        this.statusSet(file.id, 'merging');
        const api = '/api/resource/fileUpload/all';
        api.mergeSimpleUpload({
          tempName: res.tempName,
          fileName: file.name,
          ...this.params,
        }).then(res => {
          // 文件合并成功
          Bus.$emit('fileSuccess', file);
          this.statusRemove(file.id);
        }).catch(e => { });
        // 不需要合并
      } else {
        Bus.$emit('fileSuccess', file);
      }
    },
    onFileError (rootFile, file, response, chunk) {
      this.$message.error(response);

    },
    /**
     * 计算md5，实现断点续传及秒传
     * @param file
     */
    computeMD5 (file) {
      let _this = this;
      let fileReader = new FileReader();
      // let time = new Date().getTime();
      let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = 2 * 1024 * 1024; // 2M
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();
      // 文件状态设为"计算MD5"
      _this.statusSet(file.id, 'md5');
      // 暂停上传
      file.pause();
      loadNext();
      fileReader.onload = function(e) {
        spark.append(e.target.result);
        if (currentChunk < chunks) {
          // let param = {};
          // param[currentChunk] = spark.end();
          // _this.chunkMD5List.push(param);
          currentChunk++;
          // 实时展示MD5的计算进度
          _this.$nextTick(() => {
            // 校验MD5
            $(`.myStatus_${file.id}`).text(_this.$t('systerms.recovery.recovery_validMd5') + '  ' + ((currentChunk / chunks) * 100).toFixed(0) + '%');
          });
          loadNext();
        } else {
          let md5 = spark.end();
          _this.computeMD5Success(md5, file);
        }
      };
      fileReader.onerror = function() {
        // '文件$(file.name)读取出错，请检查该文件'
        _this.$message.error(_this.$t('systerms.recovery.recovery_file') + file.name + _this.$t('systerms.recovery.recovery_fileRedderError'));
        file.cancel();
      };
      function loadNext () {
        let start = currentChunk * chunkSize;
        let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },
    computeMD5Success (md5, file) {
      // 将自定义参数直接加载uploader实例的opts上
      Object.assign(this.uploader.opts, {
        query: {
          ...this.params
        }
      });
      file.uniqueIdentifier = md5;
      // 继续上传
      file.resume();
      // 自定义状态去除
      this.statusRemove(file.id);
    },
    fileListShow () {
      this.collapse = !this.collapse;
      // let $list = $('#global-uploader .file-list');
      // if ($list.is(':visible')) {
      //   // 隐藏面板
      //   // $list.slideUp();
      //   this.collapse = true;
      // } else {
      //   // 显现面板
      //   $list.slideDown();
      //   this.collapse = false;
      // }
    },
    /**
     * 新增的自定义的状态: 增加了“校验MD5”、“合并中”、“转码中”、“上传失败”这几种自定义的状态
     * @param id
     * @param status
     */
    // 自定义一个状态，生成一个p标签盖在原本的状态上面
    statusSet (id, status) {
      let statusMap = {
        // 校验MD5
        md5: {
          text: this.$t('systerms.recovery.recovery_validMd5'),
          bgc: '#fff'
        },
        // 合并中
        merging: {
          text: this.$t('systerms.recovery.recovery_mermage'),
          bgc: '#e2eeff'
        },
        // 转码中
        transcoding: {
          text: this.$t('systerms.recovery.recovery_coding'),
          bgc: '#e2eeff'
        },
        // 上传出错
        failed: {
          text: this.$t('systerms.recovery.recovery_upFileSuccessError'),
          bgc: '#e2eeff'
        }
      };
      this.$nextTick(() => {
        $(`<p class="myStatus_${id}"></p>`).appendTo(`.file_${id} .uploader-file-status`).css({
          'position': 'absolute',
          'top': '0',
          'left': '0',
          'right': '0',
          'bottom': '0',
          'zIndex': '1',
          'marginRight': '5',
          'backgroundColor': statusMap[status].bgc
        }).text(statusMap[status].text);
      });
    },
    // 自定义状态结束时，移除该标签。
    statusRemove (id) {
      this.$nextTick(() => {
        $(`.myStatus_${id}`).remove();
      });
    }
  }
};
</script>

<style scoped lang="scss">
#global-uploader {
  position: fixed;
  z-index: 20;
  right: 15px;
  bottom: 15px;
  .uploader-app {
    width: 520px;
  }
  .file-panel {
    background-color: #fff;
    border: 1px solid #e2e2e2;
    border-radius: 7px 7px 0 0;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
    .file-title {
      display: flex;
      height: 40px;
      line-height: 40px;
      padding: 0 15px;
      border-bottom: 1px solid #ddd;
      .operate {
        flex: 1;
        text-align: right;
      }
    }
    .file-list {
      position: relative;
      height: 240px;
      overflow-x: hidden;
      overflow-y: auto;
      background-color: #fff;
      > li {
        background-color: #fff;
      }
    }
    &.collapse {
      .file-title {
        background-color: #e7ecf2;
      }
    }
  }
  .no-file {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 16px;
  }
  :deep(.uploader-file-icon) {
    &:before {
      content: "" !important;
    }
  }
  :deep(.uploader-file-actions > span) {
    margin-right: 6px;
  }
}
/* 隐藏上传按钮 */
#global-uploader-btn {
  position: absolute;
  clip: rect(0, 0, 0, 0);
}
.icon-panel {
  position: fixed;
  z-index: 10;
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 50%;
  width: 48px;
  height: 48px;
  right: 35px;
  bottom: 35px;
  cursor: pointer;
  box-shadow: 0 2px 15px #0000004d;
  background-color: #1993ff;
}
</style>
